﻿using System;
using System.Data;
using System.Web.Caching;
using System.Text.RegularExpressions;
using System.Web;
using System.IO;
using System.Xml;
using System.Collections.Generic;

namespace Eagle.Common
{
    /// <summary>
    /// Xml操作类
    /// </summary>
    public class EagleXml
    {
        #region 获取节点值，内部使用
        /// <summary>
        /// 获取节点值，内部使用
        /// </summary>
        /// <param name="xmlDoc">xmldocument对象</param>
        /// <param name="xpath">节点路径</param>
        /// <returns></returns>
        private static string GetSingleNodeText(XmlDocument xmlDoc, string xpath)
        {
            XmlElement xe = (XmlElement)xmlDoc.SelectSingleNode(xpath);
            if (xe != null)
                return xe.InnerText;
            return "";
        }
        #endregion

        #region 获取XmlDocument对象，内部使用
        /// <summary>
        /// 获取XmlDocument对象，内部使用
        /// </summary>
        /// <param name="filePath">XML文件的路径</param>
        /// <returns></returns>
        private static XmlDocument GetXmlDocument(string filePath)
        {
            if (!File.Exists(filePath))
                throw new FileNotFoundException("xml文件不存在", filePath);
            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.Load(filePath);
            return xmlDoc;
        }
        #endregion

        #region 去除xml节点名称中不规范字符，内部使用
        /// <summary>
        /// 去除xml节点名称中不规范字符，内部使用
        /// </summary>
        /// <param name="nodeName">节点名称</param>
        /// <returns></returns>
        private static string ReplaceXmlNodeNameDenyChar(string nodeName)
        {
            nodeName = System.Text.RegularExpressions.Regex.Replace(nodeName, @"( )+", "");
            if (nodeName.Length == 0)
                throw new ArgumentNullException(nodeName, "节点名称不能为空");
            if (!Regex.IsMatch(nodeName.Substring(0, 1), @"[a-z]", RegexOptions.IgnoreCase))
                throw new ArgumentNullException(nodeName, "节点名称第一个字符必须是英文字符");
            return nodeName;
        }
        #endregion

        #region 去除xml节点值中的控制字符，内部使用
        /// <summary>
        /// 去除xml节点值中的控制字符,为ascii中的0-8（0x00-0x08），11-12(0x0b-0x0c)，14-31(0x0e-0x1f)，内部使用
        /// </summary>
        /// <param name="value">输入字符串</param>
        /// <returns></returns>
        private static string ReplaceXmlNodeValueDenyChar(string value)
        {
            return Regex.Replace(value, "[\x00-\x08|\x0b-\x0c|\x0e-\x1f]", "");
        }
        #endregion

        #region 保存xmldocument到磁盘，内部使用
        /// <summary>
        /// 保存xml文件，内部使用
        /// </summary>
        /// <param name="filePath">文件的物理路径</param>
        /// <param name="xmlDoc">xmldocument对象</param>
        private static void SaveXmlDocument(string filePath, XmlDocument xmlDoc)
        {
            File.SetAttributes(filePath, FileAttributes.Archive);
            xmlDoc.Save(filePath);
        }
        #endregion

        #region 生成新的xml文件，为elements，内部使用
        /// <summary>
        /// 生成xml文件，为elements，内部使用
        /// </summary>
        /// <param name="elementsName">要生成的节点名称。字符串组,如 new string[]{"节点1","节点2","节点3"}</param>
        /// <param name="elementsValue">要生成的节点值。字符串组,如 new string[]{"节点值1","节点值2","节点值3"}</param>
        /// <param name="filePath">生成XML文件的物理路径</param>
        /// <param name="isCDATA">是否是使用cdata格式，一般字符串较大且还有较多html标签，适合用cdata包含起来，ture--使用cdata格式，false--不使用cdata格式</param>
        /// <returns></returns>
        private static void CreateNewXmlForElements(string[] elementsName, string[] elementsValue, string filePath, bool isCDATA)
        {
            if (elementsName.Length != elementsValue.Length)
                throw new ArgumentOutOfRangeException("elementsName,elementsValue", "提供的字符串组的长度必须相等");
            EagleUtils.CreateDirectory(filePath);
            XmlWriterSettings xws = new XmlWriterSettings();
            xws.Indent = true;
            xws.IndentChars = "  ";
            if (File.Exists(filePath))
                File.SetAttributes(filePath, FileAttributes.Archive);
            using (XmlWriter xw = XmlWriter.Create(filePath, xws))
            {
                xw.WriteStartDocument(true);
                xw.WriteComment("该原始文档于 " + DateTime.Now.ToString() + " 由程序生成");
                xw.WriteStartElement("rootnode");
                for (int i = 0; i < elementsName.Length; i++)
                {
                    if (isCDATA)
                    {
                        xw.WriteStartElement(ReplaceXmlNodeNameDenyChar(elementsName[i].ToString()));
                        xw.WriteCData(ReplaceXmlNodeValueDenyChar(elementsValue[i].ToString()));
                        xw.WriteEndElement();
                    }
                    else
                    {
                        xw.WriteElementString(ReplaceXmlNodeNameDenyChar(elementsName[i].ToString()), ReplaceXmlNodeValueDenyChar(elementsValue[i].ToString()));
                    }
                }
                xw.WriteEndElement();
                xw.WriteEndDocument();
            }
        }
        #endregion

        #region 生成新的xml文件，为attributes，内部使用
        /// <summary>
        /// 生成xml文件，为attributes，内部使用
        /// </summary>
        /// <param name="attributesName">要生成的节点名称。字符串组,如 new string[]{"节点1","节点2","节点3"}</param>
        /// <param name="attributesValue">要生成的节点值。字符串组,如 new string[]{"节点值1","节点值2","节点值3"}</param>
        /// <param name="filePath">生成XML文件的物理路径</param>
        /// <returns></returns>
        private static void CreateNewXmlForAttributes(string[] attributesName, string[] attributesValue, string filePath)
        {
            if (attributesName.Length != attributesValue.Length)
                throw new ArgumentOutOfRangeException("attributesName,attributesValue", "提供的字符串组的长度必须相等");
            EagleUtils.CreateDirectory(filePath);
            XmlWriterSettings xws = new XmlWriterSettings();
            xws.Indent = true;
            xws.IndentChars = "  ";
            if (File.Exists(filePath))
                File.SetAttributes(filePath, FileAttributes.Archive);
            using (XmlWriter xw = XmlWriter.Create(filePath, xws))
            {
                xw.WriteStartDocument(true);
                xw.WriteComment("该原始文档于 " + DateTime.Now.ToString() + " 由程序生成");
                xw.WriteStartElement("rootnode");
                for (int i = 0; i < attributesName.Length; i++)
                {
                    xw.WriteStartElement("childnode");
                    string[] everyChildText = attributesName[i].Split(',');
                    string[] everyChildValue = attributesValue[i].Split(',');
                    for (int j = 0; j < everyChildText.Length; j++)
                    {
                        xw.WriteAttributeString(ReplaceXmlNodeNameDenyChar(everyChildText[j].ToString()), ReplaceXmlNodeValueDenyChar(everyChildValue[j].ToString()));
                    }
                    xw.WriteEndElement();
                }
                xw.WriteEndElement();
                xw.WriteEndDocument();
            }
        }
        #endregion

        #region 插入xml节点，插入elements元素，内部使用
        /// <summary>
        /// 插入xml节点，插入element元素，内部使用
        /// </summary>
        /// <param name="elementsName">节点名称</param>
        /// <param name="elementsValue">节点值</param>
        /// <param name="xmlDoc">xmldocument对象</param>
        /// <param name="xpath">节点路径</param>
        /// <param name="isCDATA">是否是使用cdata格式，一般字符串较大且还有较多html标签，适合用cdata包含起来，ture--使用cdata格式，false--不使用cdata格式</param>
        /// <param name="insertType">插入节点枚举值</param>
        /// <returns></returns>
        private static XmlDocument AppendXmlElement(string[] elementsName, string[] elementsValue, XmlDocument xmlDoc, string xpath, bool isCDATA, InsertType insertType)
        {
            if (elementsName.Length != elementsValue.Length)
                throw new ArgumentOutOfRangeException("elementsName,elementsValue", "提供的字符串组的长度必须相等");
            XmlNode xmlNode = null;
            if (xpath.Trim().Length == 0)
            {
                xmlNode = xmlDoc.DocumentElement;
            }
            else
            {
                xmlNode = xmlDoc.SelectSingleNode(xpath);
                if (xmlNode == null)
                    throw new ArgumentNullException(xpath, "并不存在该节点");
            }
            for (int i = 0; i < elementsName.Length; i++)
            {
                if (isCDATA)
                {

                    XmlCDataSection xmlcdata = xmlDoc.CreateCDataSection(ReplaceXmlNodeValueDenyChar(elementsValue[i].ToString()));
                    XmlElement xe = xmlDoc.CreateElement(ReplaceXmlNodeNameDenyChar(elementsName[i].ToString()));
                    xe.AppendChild(xmlcdata);
                    if (insertType.Equals(InsertType.FirstIndex))
                    { xmlNode.PrependChild(xe); }
                    else
                    { xmlNode.AppendChild(xe); }
                }
                else
                {
                    XmlElement xe = xmlDoc.CreateElement(ReplaceXmlNodeNameDenyChar(elementsName[i].ToString()));
                    xe.InnerText = ReplaceXmlNodeValueDenyChar(elementsValue[i].ToString());
                    if (insertType.Equals(InsertType.FirstIndex))
                    { xmlNode.PrependChild(xe); }
                    else
                    { xmlNode.AppendChild(xe); }
                }
            }
            return xmlDoc;
        }
        #endregion

        #region 插入xml节点，为attributes使用，内部使用
        /// <summary>
        /// 插入xml节点，插入attributes节点，内部使用
        /// </summary>
        /// <param name="attributesName">节点名称</param>
        /// <param name="attributesValue">节点值</param>
        /// <param name="xmlDoc">xmldocument对象</param>
        /// <param name="xpath">节点路径</param>
        /// <param name="insertType">插入节点枚举值</param>
        /// <returns></returns>
        private static XmlDocument AppendXmlAttribute(string[] attributesName, string[] attributesValue, XmlDocument xmlDoc, string xpath, InsertType insertType)
        {
            if (attributesName.Length != attributesValue.Length)
                throw new ArgumentOutOfRangeException("attributesName,attributesValue", "提供的字符串组的长度必须相等");
            XmlNode xmlNode = null;
            if (xpath.Trim().Length == 0)
            {
                xmlNode = xmlDoc.DocumentElement;
            }
            else
            {
                xmlNode = xmlDoc.SelectSingleNode(xpath);
                if (xmlNode == null)
                    throw new ArgumentNullException(xpath, "并不存在该节点");
            }
                for (int i = 0; i < attributesName.Length; i++)
                {
                    XmlElement xe = xmlDoc.CreateElement("childnode");
                    string[] everyChildText = attributesName[i].Split(',');
                    string[] everyChildValue = attributesValue[i].Split(',');
                    for (int j = 0; j < everyChildText.Length; j++)
                    {

                        XmlAttribute xa = xmlDoc.CreateAttribute(ReplaceXmlNodeNameDenyChar(everyChildText[j].ToString()));
                        xa.Value = ReplaceXmlNodeValueDenyChar(everyChildValue[j].ToString());
                        xe.SetAttributeNode(xa);
                    }
                    if (insertType.Equals(InsertType.FirstIndex))
                    { xmlNode.PrependChild(xe); }
                    else
                    { xmlNode.AppendChild(xe); }
                }
            return xmlDoc;
        }
        #endregion

        #region 插入节点枚举值
        /// <summary>
        /// 插入节点枚举值
        /// </summary>
        public enum InsertType
        {
            /// <summary>
            /// 节点群的第一个位置
            /// </summary>
            FirstIndex,
            /// <summary>
            /// 节点群的最后位置
            /// </summary>
            EndIndex
        }
        #endregion

        #region 获取XML文件某个节点的值，采用XmlDocument
        /// <summary>
        /// 获取XML文件某个节点的值，采用XmlDocument
        /// </summary>
        /// <param name="filePath">XML文件物理路径</param>
        /// <param name="xpath">单个节点的名称，如：/rootnode/childnode</param>
        /// <returns>返回节点值</returns>
        public static string GetSingleNodeText(string filePath, string xpath)
        {
            XmlDocument xmlDoc = GetXmlDocument(filePath);
            return GetSingleNodeText(xmlDoc, xpath);
        }

        /// <summary>
        /// 获取XML文件某个节点的值，采用XmlDocument
        /// </summary>
        /// <param name="filePath">XML文件物理路径</param>
        /// <param name="xpath">单个节点的名称，如：/rootnode/childnode</param>
        /// <param name="allowDefaultValue">是否允许返回空字符串</param>
        /// <returns>返回节点值</returns>
        public static string GetSingleNodeText(string filePath, string xpath, bool allowDefaultValue)
        {
            string value = GetSingleNodeText(filePath, xpath);
            if (value.Length == 0 && allowDefaultValue)
                throw new ArgumentNullException(filePath + ",路径：" + xpath, "不允许返回空值");
            return value;
        }

        /// <summary>
        /// 获取XML文件某个节点的值，采用XmlDocument
        /// </summary>
        /// <param name="filePath">XML文件物理路径</param>
        /// <param name="xpath">单个节点的名称，如：/rootnode/childnode</param>
        /// <param name="cacheName">缓存项（如果有缓存项将优先读取缓存项，没有的话就加载xml文件）</param>
        /// <param name="secondCacheTime">要缓存的时间，如果为0 则表示永久缓存，单位：秒</param>
        /// <returns>返回节点值</returns>
        public static string GetSingleNodeText(string filePath, string xpath, string cacheName, uint secondCacheTime)
        {
            cacheName = cacheName.ToLower();
            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc = Eagle.Cache.CacheFactory.Instance.GetCache(cacheName) as XmlDocument;
            if (xmlDoc == null)
            {
                xmlDoc = new XmlDocument();
                xmlDoc = GetXmlDocument(filePath);
                Eagle.Cache.CacheFactory.Instance.AddCache(cacheName, xmlDoc, secondCacheTime);
            }
            return GetSingleNodeText(xmlDoc, xpath);
        }
        #endregion

        #region 为elements生成XML文件

        #region 为elements生成XML文件,基础方法
        /// <summary>
        /// 为elements生成XML文件
        /// </summary>
        /// <param name="elementsName">要生成的节点名称。字符串组,如 new string[]{"节点1","节点2","节点3"}</param>
        /// <param name="elementsValue">要生成的节点值。字符串组,如 new string[]{"节点值1","节点值2","节点值3"}</param>
        /// <param name="filePath">生成XML文件的物理路径</param>
        /// <param name="newXml">是否全新生成，true--每次都全新生成，false--在根节点下追加节点及其属性,如果文件不存在也会全新创建，但是如果存在了，将会追加节点</param>
        /// <param name="xpath">节点路径xpath，要在哪个节点下追加节点,如果为空值，则将取xml的根节点，xpath节点形式：/rootnode 表示rootnode节点，/rootnode/childnode 表示rootnode节点下的childnode节点</param>
        /// <param name="isCDATA">是否是使用cdata格式，一般字符串较大且还有较多html标签，适合用cdata包含起来，ture--使用cdata格式，false--不使用cdata格式</param>
        /// <param name="insertType">插入节点的枚举值</param>
        public static void CreateXmlForElements(string[] elementsName, string[] elementsValue, string filePath, bool newXml, string xpath, bool isCDATA, InsertType insertType)
        {
            string[] value = elementsValue;
            EagleUtils.CreateDirectory(filePath);
            if (File.Exists(filePath) == true && newXml == false)
            {
                XmlDocument xmlDoc = new XmlDocument();
                xmlDoc = GetXmlDocument(filePath);
                AppendXmlElement(elementsName, elementsValue, xmlDoc, xpath, isCDATA, insertType);
                SaveXmlDocument(filePath, xmlDoc);
            }
            else
            {
                CreateNewXmlForElements(elementsName, elementsValue, filePath, isCDATA);
            }
        }
        #endregion

        #region 为elements生成XML文件，重载方法
        /// <summary>
        /// 为elements生成XML文件
        /// </summary>
        /// <param name="elementsName">要生成的节点名称</param>
        /// <param name="elementsValue">要生成的节点值</param>
        /// <param name="filePath">生成XML文件的物理路径</param>
        /// <param name="newXml">是否全新生成，true--每次都全新生成，false--在根节点下追加节点及其属性,如果文件不存在也会全新创建，但是如果存在了，将会追加节点</param>
        /// <param name="xpath">节点路径xpath，全新创建xml不需要指定该值，可为string.Empty,追加节点时使用，如果为空值，则将取xml的根节点，xpath节点形式：/rootnode 表示rootnode节点，/rootnode/childnode 表示rootnode节点下的childnode节点</param>
        /// <param name="isCDATA">是否是使用cdata格式，一般字符串较大且还有较多html标签，适合用cdata包含起来，ture--使用cdata格式，false--不使用cdata格式</param>
        public static void CreateXmlForElements(string elementsName, string elementsValue, string filePath, bool newXml, string xpath, bool isCDATA)
        {
            CreateXmlForElements(new string[] { elementsName }, new string[] { elementsValue }, filePath, newXml, xpath, isCDATA, InsertType.EndIndex);
        }
        /// <summary>
        /// 为elements生成XML文件
        /// </summary>
        /// <param name="elementsName">要生成的节点名称</param>
        /// <param name="elementsValue">要生成的节点值</param>
        /// <param name="filePath">生成XML文件的物理路径</param>
        /// <param name="newXml">是否全新生成，true--每次都全新生成，false--在根节点下追加节点及其属性,如果文件不存在也会全新创建，但是如果存在了，将会追加节点</param>
        /// <param name="xpath">节点路径xpath，要在哪个节点下追加节点,如果为空值，则将取xml的根节点，xpath节点形式：/rootnode 表示rootnode节点，/rootnode/childnode 表示rootnode节点下的childnode节点</param>
        /// <param name="isCDATA">是否是使用cdata格式，一般字符串较大且还有较多html标签，适合用cdata包含起来，ture--使用cdata格式，false--不使用cdata格式</param>
        /// <param name="insertType">插入节点的枚举值</param>
        public static void CreateXmlForElements(string elementsName, string elementsValue, string filePath, bool newXml, string xpath, bool isCDATA, InsertType insertType)
        {
            CreateXmlForElements(new string[] { elementsName }, new string[] { elementsValue }, filePath, newXml, xpath, isCDATA, insertType);
        }
        /// <summary>
        /// 为elements生成XML文件
        /// </summary>
        /// <param name="elementsName">要生成的节点名称。字符串组,如 new string[]{"节点1","节点2","节点3"}</param>
        /// <param name="elementsValue">要生成的节点值。字符串组,如 new string[]{"节点值1","节点值2","节点值3"}</param>
        /// <param name="filePath">生成XML文件的物理路径</param>
        /// <param name="newXml">是否全新生成，true--每次都全新生成，false--在根节点下追加节点及其属性,如果文件不存在也会全新创建，但是如果存在了，将会追加节点</param>
        /// <param name="xpath">节点路径xpath，要在哪个节点下追加节点,如果为空值，则将取xml的根节点，xpath节点形式：/rootnode 表示rootnode节点，/rootnode/childnode 表示rootnode节点下的childnode节点</param>
        /// <param name="isCDATA">是否是使用cdata格式，一般字符串较大且还有较多html标签，适合用cdata包含起来，ture--使用cdata格式，false--不使用cdata格式</param>
        public static void CreateXmlForElements(string[] elementsName, string[] elementsValue, string filePath, bool newXml, string xpath, bool isCDATA)
        {
            CreateXmlForElements(elementsName, elementsValue, filePath, newXml, xpath, isCDATA, InsertType.EndIndex);
        }
        #endregion

        #endregion

        #region 为DataTable生成XML文件

        #region 为DataTable生成XML文件,基础方法
        /// <summary>
        /// 为DataTable生成XML文件
        /// </summary>
        /// <param name="attributesName">自定义要生成的属性名称的字符串组</param>
        /// <param name="columnName">要生成的属性的节点值，该值是取自datatable中的值，因此此处填写datatable中的字段名</param>
        /// <param name="dt">datatable数据表</param>
        /// <param name="filePath">生成XML文件的物理路径</param>
        /// <param name="newXml">是否全新生成，true--每次都全新生成，false--在根节点下追加节点及其属性,如果文件不存在也会全新创建，但是如果存在了，将会追加节点</param>
        /// <param name="xpath">节点路径xpath，要在哪个节点下追加节点,如果为空值，则将取xml的根节点,xpath节点形式：/rootnode 表示rootnode节点，/rootnode/childnode 表示rootnode节点下的childnode节点</param> 
        /// <param name="insertType">插入节点的枚举值</param>
        public static void CreateXmlForTable(string[] attributesName, string[] columnName, DataTable dt, string filePath, bool newXml, string xpath, InsertType insertType)
        {
            EagleUtils.CreateDirectory(filePath);
            if (File.Exists(filePath) == true && newXml == false)
            {
                XmlDocument xmlDoc = GetXmlDocument(filePath);
                if (xmlDoc == null)
                    throw new XmlException("加载 " + filePath + " 文件失败");
                XmlNode rootNode = null;
                if (xpath.Trim().Length == 0)
                { rootNode = xmlDoc.DocumentElement; }
                else
                {
                    rootNode = xmlDoc.SelectSingleNode(xpath).LastChild;
                }
                foreach (DataRow dr in dt.Rows)
                {
                    XmlElement xe = xmlDoc.CreateElement("childnode");
                    for (int i = 0; i < attributesName.Length; i++)
                    {
                        XmlAttribute xa = xmlDoc.CreateAttribute(ReplaceXmlNodeNameDenyChar(attributesName[i]));
                        xa.Value = ReplaceXmlNodeValueDenyChar(dr[columnName[i]].ToString());
                        xe.SetAttributeNode(xa);
                    }
                    if (insertType.Equals(InsertType.FirstIndex))
                    { rootNode.PrependChild(xe); }
                    else
                    { rootNode.AppendChild(xe); }
                }
                SaveXmlDocument(filePath, xmlDoc);
            }
            else
            {
                XmlWriterSettings xws = new XmlWriterSettings();
                xws.Indent = true;
                xws.IndentChars = "  ";
                if (File.Exists(filePath))
                    File.SetAttributes(filePath, FileAttributes.Archive);
                using (XmlWriter xw = XmlWriter.Create(filePath, xws))
                {
                    xw.WriteStartDocument(true);
                    xw.WriteComment("该原始文档于 " + DateTime.Now.ToString() + " 由程序生成");
                    xw.WriteStartElement("rootnode");
                    foreach (DataRow dr in dt.Rows)
                    {
                        xw.WriteStartElement("childnode");
                        for (int i = 0; i < attributesName.Length; i++)
                        {
                            xw.WriteAttributeString(ReplaceXmlNodeNameDenyChar(attributesName[i].ToString()), ReplaceXmlNodeValueDenyChar(dr[columnName[i]].ToString()));
                        }
                        xw.WriteEndElement();
                    }
                    xw.WriteEndElement();
                    xw.WriteEndDocument();
                }
            }
        }
        #endregion

        #region 为DataTable生成XML文件，重载方法
        /// <summary>
        /// 为DataTable生成XML文件
        /// </summary>
        /// <param name="attributesName">自定义要生成的属性名称的字符串组</param>
        /// <param name="columnName">要生成的属性的节点值，该值是取自datatable中的值，因此此处填写datatable中的字段名</param>
        /// <param name="dt">datatable数据表</param>
        /// <param name="filePath">生成XML文件的物理路径</param>
        /// <param name="newXml">是否全新生成，true--每次都全新生成，false--在根节点下追加节点及其属性,如果文件不存在也会全新创建，但是如果存在了，将会追加节点</param>
        /// <param name="xpath">节点路径xpath，要在哪个节点下追加节点,如果为空值，则将取xml的根节点,xpath节点形式：/rootnode 表示rootnode节点，/rootnode/childnode 表示rootnode节点下的childnode节点</param> 
        public static void CreateXmlForTable(string[] attributesName, string[] columnName, DataTable dt, string filePath, bool newXml, string xpath)
        {
            CreateXmlForTable(attributesName, columnName, dt, filePath, newXml, xpath, InsertType.EndIndex);
        }
        #endregion

        #endregion

        #region 为attributes生成xml文件

        #region 自定义生成xml的属性及其值,基础方法
        /// <summary>
        /// 自定义生成xml的属性及其值
        /// </summary>
        /// <param name="attributesName">字符串组，每一组字符如果有多个属性，则采用多个用半角逗号‘,’隔开,如 属性1,属性2,属性3</param>
        /// <param name="attributesValue">字符串组，每一组字符如果有多个属性值，则采用多个用半角逗号‘,’隔开,如 属性值1,属性值2,属性值3，需要 与上面的属性一一对应</param>
        /// <param name="filePath">生成XML文件的物理路径</param>
        /// <param name="newXml">是否全新生成，true--每次都全新生成，false--在根节点下追加节点及其属性,如果文件不存在也会全新创建，但是如果存在了，将会追加节点</param>
        /// <param name="xpath">节点路径xpath，如果newXml参数为false，则该参数有用，否则置为空即可，要在哪个节点下追加节点,如果为空值，则将取xml的根节点，xpath节点形式：/rootnode 表示rootnode节点，/rootnode/childnode 表示rootnode节点下的childnode节点</param>
        /// <param name="insertType">加入节点的枚举值</param>
        public static void CreateXmlForAttributes(string[] attributesName, string[] attributesValue, string filePath, bool newXml, string xpath, InsertType insertType)
        {
            if (File.Exists(filePath) == true && newXml == false)//存在该xml文件则追加节点
            {
                XmlDocument xmlDoc = GetXmlDocument(filePath);
                xmlDoc = AppendXmlAttribute(attributesName, attributesValue, xmlDoc, xpath, insertType);
                SaveXmlDocument(filePath, xmlDoc);
            }
            else//不存在xml文件则创建新的xml文件
            {
                CreateNewXmlForAttributes(attributesName, attributesValue, filePath);
            }
        }
        #endregion

        #region 自定义生成xml的属性及其值,重载方法
        /// <summary>
        /// 自定义生成xml的属性及其值
        /// </summary>
        /// <param name="attributesName">字符串组，每一组字符如果有多个属性，则采用多个用半角逗号‘,’隔开,如 属性1,属性2,属性3</param>
        /// <param name="attributesValue">字符串组，每一组字符如果有多个属性值，则采用多个用半角逗号‘,’隔开,如 属性值1,属性值2,属性值3，需要 与上面的属性一一对应</param>
        /// <param name="filePath">生成XML文件的物理路径</param>
        /// <param name="newXml">是否全新生成，true--每次都全新生成，false--在根节点下追加节点及其属性,如果文件不存在也会全新创建，但是如果存在了，将会追加节点</param>
        /// <param name="xpath">节点路径xpath，如果newXml参数为false，则该参数有用，否则置为空即可，要在哪个节点下追加节点,如果为空值，则将取xml的根节点，xpath节点形式：/rootnode 表示rootnode节点，/rootnode/childnode 表示rootnode节点下的childnode节点</param>
        public static void CreateXmlForAttributes(string[] attributesName, string[] attributesValue, string filePath, bool newXml, string xpath)
        {
            CreateXmlForAttributes(attributesName, attributesValue, filePath, newXml, xpath, InsertType.EndIndex);
        }
        /// <summary>
        /// 自定义生成xml的属性及其值
        /// </summary>
        /// <param name="attributesName">节点属性名称</param>
        /// <param name="attributesValue">节点属性值</param>
        /// <param name="filePath">生成XML文件的物理路径</param>
        /// <param name="newXml">是否全新生成，true--每次都全新生成，false--在根节点下追加节点及其属性,如果文件不存在也会全新创建，但是如果存在了，将会追加节点</param>
        /// <param name="xpath">节点路径xpath，如果newXml参数为false，则该参数有用，否则置为空即可，要在哪个节点下追加节点,如果为空值，则将取xml的根节点，xpath节点形式：/rootnode 表示rootnode节点，/rootnode/childnode 表示rootnode节点下的childnode节点</param>
        public static void CreateXmlForAttributes(string attributesName, string attributesValue, string filePath, bool newXml, string xpath)
        {
            CreateXmlForAttributes(new string[] { attributesName }, new string[] { attributesValue }, filePath, newXml, xpath, InsertType.EndIndex);
        }
        /// <summary>
        /// 自定义生成xml的属性及其值
        /// </summary>
        /// <param name="attributesName">节点属性名称</param>
        /// <param name="attributesValue">节点属性值</param>
        /// <param name="filePath">生成XML文件的物理路径</param>
        /// <param name="newXml">是否全新生成，true--每次都全新生成，false--在根节点下追加节点及其属性,如果文件不存在也会全新创建，但是如果存在了，将会追加节点</param>
        /// <param name="xpath">节点路径xpath，如果newXml参数为false，则该参数有用，否则置为空即可，要在哪个节点下追加节点,如果为空值，则将取xml的根节点，xpath节点形式：/rootnode 表示rootnode节点，/rootnode/childnode 表示rootnode节点下的childnode节点</param>
        /// <param name="insertType">加入节点的枚举值</param>
        public static void CreateXmlForAttributes(string attributesName, string attributesValue, string filePath, bool newXml, string xpath, InsertType insertType)
        {
            CreateXmlForAttributes(new string[] { attributesName }, new string[] { attributesValue }, filePath, newXml, xpath, insertType);
        }
        #endregion

        #endregion

        #region 修改某个xml文件的节点
        /// <summary>
        /// 修改xml节点的值
        /// </summary>
        /// <param name="filePath">要修改的xml文件的物理路径</param>
        /// <param name="xpath">节点的xpath,要区分大小写，如：/rootnode/childnode</param>
        /// <param name="nodeValue">该节点的值</param>
        /// <param name="isCDATA">是否是使用cdata格式，一般字符串较大且还有较多html标签，适合用cdata包含起来，ture--使用cdata格式，false--不使用cdata格式</param>
        /// <returns></returns>
        public static bool EditXmlNode(string filePath, string xpath, string nodeValue, bool isCDATA)
        {
            return EditXmlNode(filePath, new string[] { xpath }, new string[] { nodeValue }, isCDATA);
        }
        /// <summary>
        /// 修改xml节点的值
        /// </summary>
        /// <param name="filePath">要修改的xml文件的物理路径</param>
        /// <param name="xpath">节点的xpath的集合,要区分大小写，如：/rootnode/childnode</param>
        /// <param name="nodeValue">该节点的值的集合</param>
        /// <param name="isCDATA">是否是使用cdata格式，一般字符串较大且还有较多html标签，适合用cdata包含起来，ture--使用cdata格式，false--不使用cdata格式</param>
        /// <returns></returns>
        public static bool EditXmlNode(string filePath, string[] xpath, string[] nodeValue, bool isCDATA)
        {
            XmlDocument xmlDoc = new XmlDocument();
            int j = 0;
            if (!File.Exists(filePath))
                throw new FileNotFoundException("不存在该文件", filePath);
            xmlDoc.Load(filePath);

            if (isCDATA)
            {
                for (int i = 0; i < xpath.Length; i++)
                {
                    XmlNode xmlnode = xmlDoc.SelectSingleNode(xpath[i]);
                    XmlNode previousSibling = xmlnode.PreviousSibling;
                    XmlNode parentNode = xmlnode.ParentNode;
                    xmlnode.RemoveAll();
                    xmlnode.ParentNode.RemoveChild(xmlnode);
                    string[] everyPath = xpath[i].Split('/');
                    string lastPath = everyPath[everyPath.Length - 1];
                    XmlCDataSection xmlcdata = xmlDoc.CreateCDataSection(ReplaceXmlNodeValueDenyChar(nodeValue[i]));
                    XmlElement xe = xmlDoc.CreateElement(lastPath);
                    xe.AppendChild(xmlcdata);
                    if (previousSibling == null)
                    {
                        parentNode.PrependChild(xe);
                        j++;
                    }
                    else
                    {
                        previousSibling.ParentNode.InsertAfter(xe, previousSibling);
                        j++;
                    }
                }
            }
            else
            {
                for (int i = 0; i < xpath.Length; i++)
                {
                    XmlNode xe = xmlDoc.SelectSingleNode(xpath[i].ToString());
                    if (xe != null)
                    {
                        xe.InnerText = nodeValue[i].ToString();
                        j++;
                    }
                }
            }

            if (j > 0)
            {
                SaveXmlDocument(filePath, xmlDoc);
                return true;
            }
            return false;
        }
        /// <summary>
        /// 修改xml节点的值
        /// </summary>
        /// <param name="filePath">要修改的xml文件的物理路径</param>
        /// <param name="nodeName">节点的xpath,要区分大小写，如：/rootnode/childnode</param>
        /// <param name="attributeName">当前节点属性名称集合</param>
        /// <param name="attributeValue">当前节点属性值集合</param>
        /// <returns></returns>
        public static bool EditXmlNode(string filePath, string nodeName, string[] attributeName, string[] attributeValue)
        {
            int j = 0;
            if (File.Exists(filePath))
            {
                XmlDocument xmlDoc = GetXmlDocument(filePath);
                XmlNode xe = xmlDoc.SelectSingleNode(nodeName);
                for (int i = 0; i < attributeName.Length; i++)
                {
                    XmlAttribute xa = xe.Attributes[attributeName[i].ToString()];
                    if (xa != null)
                    {
                        xa.Value = attributeValue[i].ToString();
                        j++;
                    }
                }
                if (j > 0)
                {
                    SaveXmlDocument(filePath, xmlDoc);
                    return true;
                }
            }
            return false;
        }
        #endregion

        #region 删除某个节点
        /// <summary>
        /// 删除某个节点
        /// </summary>
        /// <param name="filePath">要删除节点的xml文件物理路径</param>
        /// <param name="nodeName">节点的xpath,要区分大小写，如：/rootnode/childnode</param>
        /// <param name="deleteSelf">是否删除节点本身</param>
        /// <returns></returns>
        public static bool DeleteXmlNode(string filePath, string nodeName, bool deleteSelf)
        {
            if (File.Exists(filePath))
            {
                XmlDocument xmlDoc = GetXmlDocument(filePath);
                XmlNode xn = xmlDoc.SelectSingleNode(nodeName);
                if (xn != null)
                {
                    xn.RemoveAll();
                }
                if (deleteSelf)
                {
                    xn.ParentNode.RemoveChild(xn);
                }
                File.SetAttributes(filePath, FileAttributes.Archive);
                xmlDoc.Save(filePath);
                File.SetAttributes(filePath, FileAttributes.ReadOnly);
                return true;
            }
            return false;
        }
        #endregion

        #region 生成XmlTree，采用XmlDocument
        /// <summary>
        /// 生成xmltree，采用XmlDocument
        /// </summary>
        /// <param name="dt">数据表</param>
        /// <param name="filePath">xml文件的物理路径</param>
        /// <param name="attributeFiled">要生成的属性名称，形如：text1,text2,text3,text4,text5</param>
        /// <param name="attibuteValue">要生成的属性值，如果该值需要从数据表中读取，则这里不需要填写，如果该值是一个固定值，则需要填写，与attributeText属性的数目要匹配，形如：value1,value,value3,value4,value5</param>
        /// <param name="parentFiled">xmltree条件字段，指datatable中的递归字段的父级字段</param>
        /// <param name="childField">xmltree条件字段，指datatable中的递归字段的子级字段</param>
        /// <param name="condition">xmltree的条件，即递归中父级字段初始值</param>
        public static void CreateXmlTree(DataTable dt, string filePath, string attributeFiled, string attibuteValue, string parentFiled, string childField, string condition)
        {
            EagleUtils.CreateDirectory(filePath);
            XmlDocument xmlDoc = new XmlDocument();
            XmlDeclaration xmlDec = xmlDoc.CreateXmlDeclaration("1.0", "UTF-8", "yes");
            xmlDoc.InsertBefore(xmlDec, xmlDoc.DocumentElement);
            XmlComment xc = xmlDoc.CreateComment("该XML树文件生成于：" + DateTime.Now.ToString() + "");
            xmlDoc.AppendChild(xc);
            XmlElement xe = xmlDoc.CreateElement("rootnode");
            xmlDoc.AppendChild(xe);
            CreateXmlNode(dt, xmlDoc, xe, attributeFiled, attibuteValue, parentFiled, childField, condition);
            SaveXmlDocument(filePath, xmlDoc);
        }

        private static void CreateXmlNode(DataTable dt, XmlDocument xmlDoc, XmlElement xe, string attributeFiled, string attibuteValue, string parentFiled, string childField, string condition)
        {
            string[] everyText = attributeFiled.Split(',');
            string[] everyValue = attibuteValue.Split(',');
            DataRow[] drCollection = dt.Select(parentFiled + "='" + condition + "'");
            foreach (DataRow dr in drCollection)
            {
                XmlElement xee = xmlDoc.CreateElement("childnode");
                xe.AppendChild(xee);
                for (int i = 0; i < everyText.Length; i++)
                {
                    if (EagleUtils.IsNullOrEmpty(everyValue[i]))
                    {
                        XmlAttribute xa = xmlDoc.CreateAttribute(everyText[i].ToString());
                        xa.Value = dr[everyText[i]].ToString().Trim();
                        xee.SetAttributeNode(xa);
                    }
                    else
                    {
                        XmlAttribute xa = xmlDoc.CreateAttribute(everyText[i].ToString());
                        xa.Value = everyValue[i].ToString().Trim();
                        xee.SetAttributeNode(xa);
                    }
                }
                condition = dr[childField].ToString();
                CreateXmlNode(dt, xmlDoc, xee, attributeFiled, attibuteValue, parentFiled, childField, condition);
            }
        }
        #endregion

        #region 读取xml文件数据
        /// <summary>
        /// 读取xml文件到dataset中，返回数据表
        /// </summary>
        /// <param name="filePath">文件物理路径</param>
        /// <returns></returns>
        public static DataTable GetXmlAttributesByXmlReader(string filePath)
        {
            DataSet ds = new DataSet();
            if (!File.Exists(filePath))
                throw new FileNotFoundException("不存在该文件", filePath);
            DataTable dt = null;
            try
            {
                ds.ReadXml(EagleUtils.ConvertToPhysicalPath(filePath));
                dt = ds.Tables[0];
            }
            catch
            {
                throw new System.Security.SecurityException("读取xml架构时发生异常");
            }
            finally
            {
                if (ds != null)
                    ds.Dispose();
            }

            return dt;
        }
        /// <summary>
        /// 读取xml文件数据到泛型集合中
        /// </summary>
        /// <param name="filePath">文件物理路径</param>
        /// <param name="depth">节点深度，根节点是0，依次类推，如果要全部读取，则depth的值为-1</param>
        /// <returns></returns>
        public static List<string> GetXmlElementsByXmlReader(string filePath, int depth)
        {
            List<string> myxmldata = new List<string>(10);
            XmlReaderSettings xrs = new XmlReaderSettings();
            xrs.IgnoreComments = true;
            xrs.IgnoreWhitespace = true;
            xrs.IgnoreProcessingInstructions = true;
            if (!File.Exists(filePath))
                throw new FileNotFoundException("不存在该文件", filePath);
            using (XmlReader xr = XmlReader.Create(filePath, xrs))
            {
                if (depth == -1)
                {
                    while (xr.Read())
                    {
                        //xr.ReadStartElement();
                        //xr.ReadEndElement();

                        if (xr.NodeType == XmlNodeType.Element)
                        {
                            myxmldata.Add(xr.ReadString());
                        }
                    }
                }
                else
                {
                    while (xr.Read())
                    {
                        //xr.ReadStartElement();
                        //xr.ReadEndElement();

                        if (xr.Depth == depth && xr.NodeType == XmlNodeType.Element)
                        {
                            myxmldata.Add(xr.ReadString());
                        }
                    }
                }
            }
            return myxmldata;
        }
        /// <summary>
        /// 读取xml,采用SelectSingleNode，不适合有重复节点的情况
        /// </summary>
        /// <param name="filePath">xml文件路径</param>
        /// <param name="xpaths">节点路径集合,每个节点路径形式如：/meta/bookid 如果节点有属性值，其体现形式为/meta/bookid@type</param>
        /// <returns></returns>
        public static Dictionary<string, string> GetXmlElementsByXmlDocument(string filePath, string[] xpaths)
        {
            XmlDocument xmlDoc = GetXmlDocument(filePath);
            Dictionary<string, string> values = new Dictionary<string, string>(xpaths.Length);
            foreach (string xpath in xpaths)
            {
                XmlNode xn = xmlDoc.SelectSingleNode(xpath);
                string value = string.Empty;
                if (xn != null)
                {
                    value = xn.InnerText;
                    XmlAttributeCollection xac = xn.Attributes;
                    foreach (XmlAttribute xa in xac)
                    {
                        values.Add(xpath + "@" + xa.Name, xa.Value);
                    }
                }
                values.Add(xpath, value);
            }
            return values;
        }
        #endregion
    }
}
